package tc.de.krawallmann.gravoblocks.game;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.Sound;
import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.slick.state.transition.FadeInTransition;
import org.newdawn.slick.state.transition.FadeOutTransition;

import tc.de.krawallmann.achievement.AchievementNotifier;
import tc.de.krawallmann.event.GameOverListener;
import tc.de.krawallmann.event.OKDialogListener;
import tc.de.krawallmann.event.PauseListener;
import tc.de.krawallmann.event.ScoreListener;
import tc.de.krawallmann.extension.AchievementExtension;
import tc.de.krawallmann.extension.HighscoresExtension;
import tc.de.krawallmann.extension.KeyboardInputMapExtension;
import tc.de.krawallmann.extension.SettingsExtension;
import tc.de.krawallmann.gravoblocks.game.model.NormalGameplay;
import tc.de.krawallmann.gravoblocks.game.model.Tetris;
import tc.de.krawallmann.gravoblocks.resource.Language;
import tc.de.krawallmann.gravoblocks.score.Score;
import tc.de.krawallmann.gravoblocks.twl.widgets.GridView;
import tc.de.krawallmann.slick.game.AbstractExtendableGame;
import tc.de.krawallmann.slick.game.AbstractMMenuState;
import tc.de.krawallmann.twl.RootPane;
import tc.de.krawallmann.twl.widgets.MulticolorLabel;
import tc.de.krawallmann.twl.widgets.MulticolorLabel.Color;
import tc.de.krawallmann.twl.widgets.YesNoDialog;
import tc.de.krawallmann.twl.widgets.ZOrderPane;
import de.matthiasmann.twl.Button;
import de.matthiasmann.twl.Label;
import de.matthiasmann.twl.Widget;

/**
 * The Class NormalGameplayState contains the actual game.
 * 
 * @author KrawallMann
 * @version 1.0
 */
public class NormalGameplayState extends AbstractMMenuState implements ScoreListener,
		GameOverListener, PauseListener, OKDialogListener {
	/** The Constant MULTICOLORLABEL40. */
	protected static final String MULTICOLORLABEL40 = "multicolorlabel40"; //$NON-NLS-1$

	/** The back button. */
	private transient Button back;

	private transient boolean blockInput;

	/** The click. */
	private transient Sound click;

	/** The dialog. */
	private transient YesNoDialog dialog;

	private transient int gameoverblink;

	private transient MulticolorLabel gameOverLabel;

	private transient MulticolorLabel gameOverLabeltext;

	/** The hold grid view. */
	private transient GridView holdGridView;

	/** The "hold" label. */
	private transient MulticolorLabel holdLabel;

	/** The label which displays the level. */
	private transient Label level;

	/** The "level" label. */
	private transient MulticolorLabel levelLabel;

	/** The main grid view. */
	private transient GridView mainGridView;
	/** The "next" label. */
	private transient MulticolorLabel nextLabel;

	private transient final AchievementNotifier notifier = new AchievementNotifier();

	private transient Widget overlay;

	/** The pane. */
	private transient ZOrderPane pane;

	/** The preview grid view. */
	private transient GridView previewGridView;

	/** The label which displays the amunt of cleared lines. */
	private transient Label rows;

	/** The "rows" label. */
	private transient MulticolorLabel rowsLabel;

	/** The label which displays the current score. */
	private transient Label score;

	/** The "score" label. */
	private transient MulticolorLabel scoreLabel;

	/** The tetris object. */
	private transient Tetris tetris;

	/** The text label. */
	private transient MulticolorLabel textLabel;

	/**
	 * Instantiates a new NormalGameplayState.
	 * 
	 * @param menuID
	 *            the id
	 * @param game
	 *            the game
	 */
	public NormalGameplayState(final int menuID, final AbstractExtendableGame game) {
		super(menuID, game);
	}

	@Override
	public void cancelPressed() {
		dialog.setVisible(false);
		overlay.setVisible(false);
		setBlockAnyInput(false);
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.slick.game.AbstractMMenuState#commandKeyPressed
	 * (java.lang.String)
	 */
	@Override
	public void commandKeyPressed(final String event) {
		super.commandKeyPressed(event);
		if (!blockInput) {
			tetris.commandKeyPressed(event);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.slick.game.AbstractMMenuState#commandKeyReleased
	 * (java.lang.String)
	 */
	@Override
	public void commandKeyReleased(final String event) {
		super.commandKeyReleased(event);
		if (!blockInput) {
			tetris.commandKeyReleased(event);
			if ("back".equals(event)) { //$NON-NLS-1$
				evaluateExit();
			}
		}
	}

	@Override
	public void enter(final GameContainer container, final StateBasedGame game)
			throws SlickException {
		super.enter(container, game);
		getGame().getExtension(KeyboardInputMapExtension.class).addCommandKeyListener(this);
		((AbstractExtendableGame) game).getExtension(AchievementExtension.class)
				.addAchievementsListener(notifier);
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.slick.game.AbstractMMenuState#init(org.newdawn.
	 * slick.GameContainer, org.newdawn.slick.state.StateBasedGame)
	 */
	@Override
	public void init(final GameContainer container, final StateBasedGame game)
			throws SlickException {
		super.init(container, game);
		click = new Sound("tc/de/krawallmann/gravoblocks/resource/click.wav"); //$NON-NLS-1$

		setBackground(new Image("tc/de/krawallmann/gravoblocks/resource/mainmenu.jpg")); //$NON-NLS-1$
		tetris =
				new Tetris(12, 21, 5, 5, 5, 5, new NormalGameplay(
						((AbstractExtendableGame) game).getExtension(AchievementExtension.class),
						new Sound("tc/de/krawallmann/gravoblocks/resource/row.wav"))); //$NON-NLS-1$
		tetris.getRule().addScoreListener(this);
		tetris.getRule().addGameOverListener(this);
		tetris.getRule().addPauseListener(this);
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.twlslick.BasicTWLGameState#layoutRootPane()
	 */
	@Override
	public void layoutRootPane() {
		super.layoutRootPane();

		// Adjust the sizes
		score.adjustSize();
		rows.adjustSize();
		level.adjustSize();
		back.adjustSize();
		dialog.adjustSize();
		dialog.layout();
		levelLabel.adjustSize();
		scoreLabel.adjustSize();
		rowsLabel.adjustSize();
		holdLabel.adjustSize();
		nextLabel.adjustSize();
		mainGridView.adjustSize();
		textLabel.adjustSize();
		gameOverLabel.adjustSize();
		gameOverLabeltext.adjustSize();

		// Set the positions
		back.setPosition(750 - back.getWidth(), 500);
		score.setPosition(600, 275);
		rows.setPosition(34, 415);
		level.setPosition(34, 275);
		mainGridView.setPosition(220, 0);
		previewGridView.setPosition(600, 25);
		holdGridView.setPosition(34, 25);
		textLabel.setPosition(mainGridView.getX()
				+ (mainGridView.getWidth() - textLabel.getWidth()) / 2, mainGridView.getY()
				+ (mainGridView.getHeight() - textLabel.getHeight()) / 2);
		gameOverLabel.setPosition(
				mainGridView.getX() + (mainGridView.getWidth() - gameOverLabel.getWidth()) / 2,
				mainGridView.getY() + (mainGridView.getHeight() - gameOverLabel.getHeight()) / 2
						- 50);
		gameOverLabeltext.setPosition(mainGridView.getX()
				+ (mainGridView.getWidth() - gameOverLabeltext.getWidth()) / 2, mainGridView.getY()
				+ (mainGridView.getHeight() - gameOverLabeltext.getHeight()) / 2 + 50);
		levelLabel.setPosition(34 + (168 - levelLabel.getWidth()) / 2, 340);
		scoreLabel.setPosition(600 + (168 - scoreLabel.getWidth()) / 2, 340);
		rowsLabel.setPosition(34 + (168 - rowsLabel.getWidth()) / 2, 480);
		holdLabel.setPosition(34 + (168 - holdLabel.getWidth()) / 2, 180);
		nextLabel.setPosition(600 + (168 - nextLabel.getWidth()) / 2, 180);
		notifier.layout(getContainer().getWidth());

		overlay.setSize(getContainer().getWidth(), getContainer().getHeight());
		pane.setSize(getContainer().getWidth(), getContainer().getHeight());
		dialog.setPosition((getContainer().getWidth() - dialog.getWidth()) / 2, (getContainer()
				.getHeight() - dialog.getHeight()) / 2);
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * org.newdawn.slick.state.BasicGameState#leave(org.newdawn.slick.GameContainer
	 * , org.newdawn.slick.state.StateBasedGame)
	 */
	@Override
	public void leave(final GameContainer container, final StateBasedGame game)
			throws SlickException {
		tetris.reset();
		tetris.getRule().addScoreListener(this);
		tetris.getRule().addGameOverListener(this);
		tetris.getRule().addPauseListener(this);
		super.leave(container, game);
		getGame().getExtension(KeyboardInputMapExtension.class).removeCommandKeyListener(this);
		((AbstractExtendableGame) game).getExtension(AchievementExtension.class)
				.removeAchievementsListener(notifier);
	}

	@Override
	public void okPressed() {
		if ((tetris.getRule().getScore() > 0)
				&& !getGame().getExtension(HighscoresExtension.class).contains(Score.NORMAL,
						tetris.getRule().getScore())) {
			final EnterScoreState state =
					(EnterScoreState) getGame().getState(GravoBlocksGame.ENTERSCORESSTATE);
			state.setScore(tetris.getRule().getScore());
			state.setLines(tetris.getRule().getClearedRows());
			state.setLevel(tetris.getRule().getLevel());
			state.setGamemode(Score.NORMAL);
			setBlockAnyInput(false);
			getGame().enterState(GravoBlocksGame.ENTERSCORESSTATE, new FadeOutTransition(),
					new FadeInTransition());
		} else if (tetris.getRule().getScore() > 0) {
			setBlockAnyInput(false);
			getGame().enterState(GravoBlocksGame.VIEWSCORESSTATE, new FadeOutTransition(),
					new FadeInTransition());
		} else {
			setBlockAnyInput(false);
			getGame().enterState(GravoBlocksGame.MAINMENUSTATE, new FadeOutTransition(),
					new FadeInTransition());
		}
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.gravoblocks.game.model.GameOverListener#
	 * onGameover ()
	 */
	@Override
	public void onGameover() {
		mainGridView.setGameover(true);
		textLabel.setVisible(false);
		gameOverLabel.setVisible(true);
		gameOverLabeltext.setVisible(true);
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.PauseListener#pauseStateChanged
	 * (boolean)
	 */
	@Override
	public void pauseStateChanged(final boolean paused) {
		if (paused) {
			setText(Language.getString("NormalGameplayState.4"), MulticolorLabel.Color.GREEN); //$NON-NLS-1$
		} else {
			setText("", MulticolorLabel.Color.GREEN); //$NON-NLS-1$
		}
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.gravoblocks.game.model.ScoreListener#scoreUpdated
	 * (int, int, int)
	 */
	@Override
	public void scoreUpdated(final int level, final int score, final int rows) {
		this.score.setText(Integer.toString(score));
		this.level.setText(Integer.toString(level));
		this.rows.setText(Integer.toString(rows));
	}

	/**
	 * Sets the text.
	 * 
	 * @param text
	 *            the text
	 * @param color
	 *            the color
	 */
	public void setText(final String text, final MulticolorLabel.Color color) {
		textLabel.setText(text);
		textLabel.setColor(color);
		textLabel.adjustSize();
		textLabel.setPosition(mainGridView.getX()
				+ (mainGridView.getWidth() - textLabel.getWidth()) / 2, mainGridView.getY()
				+ (mainGridView.getHeight() - textLabel.getHeight()) / 2);
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.slick.game.AbstractMMenuState#update(org.newdawn
	 * .slick.GameContainer, org.newdawn.slick.state.StateBasedGame, int)
	 */
	@Override
	public void update(final GameContainer container, final StateBasedGame game, final int delta)
			throws SlickException {
		super.update(container, game, delta);
		tetris.update(container, game, delta);
		notifier.update(delta);
		if ((tetris.getRule().getScore() > 0)
				&& back.getTooltipContent().equals(Language.getString("NormalGameplayState.1"))) { //$NON-NLS-1$
			back.setTooltipContent(Language.getString("NormalGameplayState.2")); //$NON-NLS-1$
		} else if (back.getTooltipContent().equals(Language.getString("NormalGameplayState.2"))) { //$NON-NLS-1$
			back.getTooltipContent().equals(Language.getString("NormalGameplayState.1")); //$NON-NLS-1$
		}
		if (tetris.getRule().isGameOver()) {
			gameoverblink += delta;
			if ((gameoverblink > 500) && (gameOverLabel.getColor() == MulticolorLabel.Color.RED)) {
				gameOverLabel.setColor(MulticolorLabel.Color.ORANGE);
				gameOverLabeltext.setColor(MulticolorLabel.Color.ORANGE);
			} else if ((gameoverblink > 1000)
					&& (gameOverLabel.getColor() == MulticolorLabel.Color.ORANGE)) {
				gameOverLabel.setColor(MulticolorLabel.Color.YELLOW);
				gameOverLabeltext.setColor(MulticolorLabel.Color.YELLOW);
			} else if ((gameoverblink > 1500)
					&& (gameOverLabel.getColor() == MulticolorLabel.Color.YELLOW)) {
				gameOverLabel.setColor(MulticolorLabel.Color.RED);
				gameOverLabeltext.setColor(MulticolorLabel.Color.RED);
				gameoverblink = 0;
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.twlslick.BasicTWLGameState#createRootPane()
	 */
	@Override
	protected RootPane createRootPane() {
		final RootPane rootPane = super.createRootPane();

		// Set root theme
		pane = new ZOrderPane();
		pane.setTheme(""); //$NON-NLS-1$
		rootPane.setTheme(""); //$NON-NLS-1$

		// Create the widgets
		dialog = new YesNoDialog(Language.getString("NormalGameplayState.0"), this); //$NON-NLS-1$
		dialog.setColor(Color.BLUE);
		dialog.setTheme("/reddialog");
		overlay = new Widget();
		overlay.setVisible(false);
		overlay.setTheme("/overlay"); //$NON-NLS-1$
		mainGridView = new GridView(tetris.getMainGrid());
		mainGridView.setTheme("gridviewtopopen"); //$NON-NLS-1$
		previewGridView = new GridView(tetris.getPreviewGrid());
		holdGridView = new GridView(tetris.getHoldGrid());

		final boolean blinkingGhost = SettingsExtension.getSetting(GravoBlocksGame.BLINKING_GHOST);
		mainGridView.init(blinkingGhost);
		previewGridView.init(blinkingGhost);
		holdGridView.init(blinkingGhost);

		back = new Button(Language.getString("NormalGameplayState.8")); //$NON-NLS-1$
		back.setTheme("button40"); //$NON-NLS-1$
		back.setTooltipContent(Language.getString("NormalGameplayState.1")); //$NON-NLS-1$

		score = new Label("0"); //$NON-NLS-1$
		score.setTheme("gridlabel"); //$NON-NLS-1$

		rows = new Label("0"); //$NON-NLS-1$
		rows.setTheme("gridlabel"); //$NON-NLS-1$

		level = new Label("1"); //$NON-NLS-1$
		level.setTheme("gridlabel"); //$NON-NLS-1$

		levelLabel = new MulticolorLabel(Language.getString("NormalGameplayState.16")); //$NON-NLS-1$
		levelLabel.setColor(MulticolorLabel.Color.GREEN);
		levelLabel.setTheme(NormalGameplayState.MULTICOLORLABEL40);

		scoreLabel = new MulticolorLabel(Language.getString("NormalGameplayState.17")); //$NON-NLS-1$
		scoreLabel.setColor(MulticolorLabel.Color.GREEN);
		scoreLabel.setTheme(NormalGameplayState.MULTICOLORLABEL40);

		rowsLabel = new MulticolorLabel(Language.getString("NormalGameplayState.18")); //$NON-NLS-1$
		rowsLabel.setColor(MulticolorLabel.Color.GREEN);
		rowsLabel.setTheme(NormalGameplayState.MULTICOLORLABEL40);

		holdLabel = new MulticolorLabel(Language.getString("NormalGameplayState.19")); //$NON-NLS-1$
		holdLabel.setColor(MulticolorLabel.Color.GREEN);
		holdLabel.setTheme(NormalGameplayState.MULTICOLORLABEL40);

		nextLabel = new MulticolorLabel(Language.getString("NormalGameplayState.20")); //$NON-NLS-1$
		nextLabel.setColor(MulticolorLabel.Color.GREEN);
		nextLabel.setTheme(NormalGameplayState.MULTICOLORLABEL40);

		textLabel = new MulticolorLabel(""); //$NON-NLS-1$
		textLabel.setColor(MulticolorLabel.Color.RED);
		textLabel.setTheme("multicolorlabel60"); //$NON-NLS-1$

		gameOverLabel = new MulticolorLabel(Language.getString("NormalGameplayState.3"));//$NON-NLS-1$
		gameOverLabel.setColor(MulticolorLabel.Color.RED);
		gameOverLabel.setTheme("multicolorlabel60"); //$NON-NLS-1$
		gameOverLabel.setVisible(false);

		gameOverLabeltext = new MulticolorLabel(Language.getString("NormalGameplayState.6")); //$NON-NLS-1$
		gameOverLabeltext.setColor(MulticolorLabel.Color.RED);
		gameOverLabeltext.setTheme(NormalGameplayState.MULTICOLORLABEL40);
		gameOverLabeltext.setVisible(false);

		// Add callbacks
		back.addCallback(new Runnable() {
			@Override
			public void run() {
				if (SettingsExtension.getSetting(GravoBlocksGame.USE_SOUND)) {
					click.play();
				}
				evaluateExit();
			}

		});

		dialog.setVisible(false);
		overlay.setVisible(false);
		// Add the widgets
		pane.add(mainGridView);
		pane.add(previewGridView);
		pane.add(holdGridView);
		pane.add(textLabel);
		pane.add(back);
		pane.add(score);
		pane.add(rows);
		pane.add(level);
		pane.add(levelLabel);
		pane.add(scoreLabel);
		pane.add(rowsLabel);
		pane.add(holdLabel);
		pane.add(nextLabel);
		pane.add(gameOverLabel);
		pane.add(gameOverLabeltext);
		pane.add(overlay);
		pane.add(dialog);
		pane.add(notifier.createAchievementLabel());
		pane.moveToFront(overlay);
		pane.moveToFront(dialog);
		rootPane.add(pane);
		// Make the widgets focusable
		return rootPane;
	}

	/**
	 * Evaluate exit.
	 */
	protected void evaluateExit() {
		if (!tetris.getRule().isGameOver()) {
			tetris.getRule().setPauseRequest(true);
			setBlockAnyInput(true);
			dialog.setVisible(true);
			overlay.setVisible(true);
		} else {
			if ((tetris.getRule().getScore() > 0)
					&& !getGame().getExtension(HighscoresExtension.class).contains(Score.NORMAL,
							tetris.getRule().getScore())) {
				final EnterScoreState state =
						(EnterScoreState) getGame().getState(GravoBlocksGame.ENTERSCORESSTATE);
				state.setScore(tetris.getRule().getScore());
				state.setLines(tetris.getRule().getClearedRows());
				state.setLevel(tetris.getRule().getLevel());
				state.setGamemode(Score.NORMAL);
				setBlockAnyInput(false);
				getGame().enterState(GravoBlocksGame.ENTERSCORESSTATE, new FadeOutTransition(),
						new FadeInTransition());
			} else if (tetris.getRule().getScore() > 0) {
				setBlockAnyInput(false);
				getGame().enterState(GravoBlocksGame.VIEWSCORESSTATE, new FadeOutTransition(),
						new FadeInTransition());
			} else {
				setBlockAnyInput(false);
				getGame().enterState(GravoBlocksGame.MAINMENUSTATE, new FadeOutTransition(),
						new FadeInTransition());
			}
		}
	}

	protected void setBlockAnyInput(final boolean blockInput) {
		this.blockInput = blockInput;
	}
}
